<!doctype html>
<!--
Copyright 2016 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
  <head>
    <title>Bluetooth Rename Tool</title>
    <meta name="description" content="Rename a Bluetooth Smart device with Web Bluetooth.">

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">


    <script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>

    <!-- Polymer components -->
    <link rel="import" href="bower_components/paper-button/paper-button.html">
    <link rel="import" href="bower_components/paper-card/paper-card.html">
    <link rel="import" href="bower_components/paper-dialog/paper-dialog.html">
    <link rel="import" href="bower_components/paper-input/paper-input.html">
    <link rel="import" href="bower_components/paper-progress/paper-progress.html">

    <link rel="import" href="bower_components/paper-styles/color.html">
    <link rel="stylesheet" href="bower_components/paper-styles/demo.css">

    <style is="custom-style">
      paper-button {
        display: block;
        margin-bottom: 24px;
      }
      paper-button.colorful {
        color: #4285f4;
      }
      paper-button[raised].colorful {
        background: #4285f4;
        color: #fff;
      }
      paper-button[toggles] {
        transition: background-color 0.3s;
      }
      paper-button[toggles][active] {
        background-color: rgba(0, 0, 0, 0.25);
      }
      paper-button[toggles][active].colorful {
        background-color: rgba(66, 133, 244, 0.25);
      }
      paper-button[toggles][active][raised].colorful {
        background-color: rgba(66, 133, 244, 0.75);
      }
      paper-button.blue {
        color: var(--paper-light-blue-500);
        paper-button-flat-focus-color: var(--paper-light-blue-50);
      }
      body {
        background-color: var(--paper-grey-50);
      }
      #cards {
        margin-left: auto;
        margin-right: auto;
        max-width: 400px;
      }
      paper-card {
        margin-bottom: 16px;
        width: 100%;
      }
      paper-progress {
        width: 100%;
      }
    </style>
  </head>
  <body unresolved>
    <div id="cards">
      <paper-card heading="Bluetooth Rename Tool">
        <div class="card-content">
          <paper-progress id="progress" indeterminate hidden="true"></paper-progress>
        </div>
      </paper-card>

      <paper-card>
        <div class="card-content">
          <paper-button id="connectButton" raised class="colorful">Connect</paper-button>
        </div>
      </paper-card>

      <paper-card>
        <div class="card-content">
          <paper-input id="nameInput" label="Device Name" disabled></paper-input>
          <paper-button id="writeNameButton" class="colorful" disabled>Update Name</paper-button>
        </div>
      </paper-card>

      <paper-dialog id="errorDialog">
        <h2>Error</h2>
        <p id="dialogError">Could not connect to bluetooth device!</p>
      </paper-dialog>

      <paper-dialog id="doneDialog">
        <h2>Done!</h2>
      </paper-dialog>
    </div>

    <script>
      "use strict";
      document.addEventListener('WebComponentsReady', function() {
        function logProgress(x) {
          console.log("Progress:", x);
          return x;
        }

        function handleError(error) {
          console.log(error);
          if (error.toString().match(/User cancelled/))
            return;  // User didn't make a choice, no error needed.
          else
            dialogError.innerText = error;
          errorDialog.open();
        }

        function disableInput() {
          nameInput.disabled = true;
          nameInput.charCounter = false;
          writeNameButton.disabled = true;
        }

        function enableInput() {
          nameInput.disabled = false;
          nameInput.charCounter = true;
          writeNameButton.disabled = false;
        }

        function anyDeviceFilter() {
          // This is the closest we can get for now to get all devices.
          // https://github.com/WebBluetoothCG/web-bluetooth/issues/234
          return Array.from('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
              .map(c => ({namePrefix: c}))
              .concat({name: ''});
        }

        connectButton.addEventListener('click', function () {
          progress.hidden = false;
          disableInput();
          Promise.resolve()
          .then(_ => {
            if (!navigator.bluetooth)
              throw "No Web Bluetooth support.";

            return navigator.bluetooth.requestDevice({
              filters: anyDeviceFilter(),
              optionalServices: ['generic_access']
            })
          })
          .then(logProgress)
          .then(device => {
            nameInput.value = "";
            return device.gatt.connect().catch(error => {
              logProgress(error);
              throw "Unable to connect. Some devices refuse connections.";
            });
          })
          .then(logProgress)
          .then(server => server.getPrimaryService("generic_access"))
          .then(logProgress)
          .then(service => service.getCharacteristic("gap.device_name"))
          .then(logProgress)
          .then(characteristic => {
            window.deviceName = characteristic;
            return characteristic.readValue();
          })
          .then(logProgress)
          .then(value => {
            window.value = value;
            nameInput.value = new TextDecoder("utf-8").decode(value);
            if (window.deviceName.properties.write)
              enableInput();
            else
              throw "Name is not writable on this device.";
          })
          .catch(handleError)
          .then(_ => progress.hidden = true)
        });

        writeNameButton.addEventListener('click', function () {
          progress.hidden = false;
          Promise.resolve()
          .then(_ => {
            let uint8arrayName = new TextEncoder("utf-8").encode(nameInput.value);
            console.log("Writing: ", uint8arrayName);
            return deviceName.writeValue(uint8arrayName)
          })
          .then(logProgress)
          .then(_ => doneDialog.open())
          .catch(handleError)
          .then(_ => progress.hidden = true)
        });

      });
    </script>
  </body>
</html>
